const { clone, printLine, fill, fillRange } = require('./util')
const _ = require('lodash');

let isShow;

const knownLine = (base, hint) => {
    let sum = _.sum(hint) + hint.length - 1; //eg 8 7+4-1=10
    let left = base.length - sum; //eg 2 0
    let accum = 0;

    if (isShow) {
        console.log();
    }

    for (let i = 0; i < hint.length; i++) {
        fillRange(base, 1, accum + left, accum + hint[i] - 1);
        accum += hint[i] + 1;
    }

    return base;
}

const _edgePositivePredict = (base, hint) => {
    let record = clone(base);
    let count = 0, hintOffset = 0;

    for (let i = 0, flag = true; i < record.length && flag; i++) {
        switch (record[i]) {
            case -1:
                if (count > 0) {
                    count = 0;
                    hintOffset++;
                }
                break;
            case 0:
                if (count === 0) {
                    flag = false;
                } else {
                    if (count < hint[hintOffset]) {
                        record[i] = 1;
                        count++;
                    } else {
                        record[i] = -1;
                        count = 0;
                        hintOffset++;
                    }
                }
                break;
            case 1:
                count++;
                break;
            default:
                break;
        }
    }

    return record;
}

//Type: 0 1| 0 -1|  -1 -1/1 | 1 1/-1
// 0 -1 -> Check if enough for 1st hint
// 0 1 -> Check if back close one.
const _edgeNegativePredict = (base, hint) => {
    let record = clone(base);
    let count = 0, zeroCount = 0, hintOffset = 0;

    for (let i = 0, flag = true; i < record.length && flag; i++) {
        switch (record[i]) {
            case -1:
                if (zeroCount > 0) {
                    if (count > 0) { // e.g.: _ 1 x ....
                        if (hint[hintOffset] + 1 > zeroCount) {
                            for (let j = i - count - 1; j > i - hint[hintOffset] - 1; j--) {
                                record[j] = 1;
                            }
                        }
                        flag = false;
                    } else { // e.g.: _ _ x ....
                        if (hint[hintOffset] > zeroCount) {
                            for (let j = i - 1; j > i - zeroCount - 1; j--) {
                                record[j] = -1;
                            }
                        }
                        flag = false;
                    }
                } else {
                    if (count > 0) { // e.g.: 1 x ...
                        count = 0;
                        hintOffset++;
                    }
                    //else do nothing. e.g.: x ...
                }
                break;
            case 0:
                if (count === 0) {// e.g.: 1 x 0;  x 0; 0 0;
                    zeroCount++;
                } else {// 0 1 0; 1 0;
                    if (count + zeroCount > hint[hintOffset] && hint[hintOffset] + 1 > zeroCount) {
                        // e.g.: [1] 0 1 0; [6] 0 0 0 0 0 1 1 0(i)
                        // [6] x x 0 0 0 0 0 1 1 1 _ 
                        // i = 11; count = 3; zCount = 5; hint[offset] = 6;
                        // start = 11 - 3 - 5 = 3
                        let _count = count + zeroCount - hint[hintOffset];
                        let start = i - count - zeroCount;
                        fillRange(record, -1, start, start + _count - 1);
                        // for (let j = 0; j < _count; j++) {
                        //     record[start + j] = -1;
                        // }
                    } else {
                    }
                    flag = false;
                }
                break;
            case 1:
                count++;
                break;
            default:
                break;
        }
    }

    if (hintOffset === hint.length && record.indexOf(0) !== -1) {
        // fillRange(record, -1);
        for (let i = 0; i <= record.length; i++) {
            if (record[i] === 0) {
                fill(record, -1, i);
            }
        }
    }

    return record;
}

const edgeLine = (base, hint) => {
    let _actual = clone(base);

    _actual = _edgePositivePredict(_actual, hint)
    _actual = _edgeNegativePredict(_actual, hint)

    _actual = _actual.reverse();
    hint = hint.reverse();

    _actual = _edgePositivePredict(_actual, hint)
    _actual = _edgeNegativePredict(_actual, hint)

    return _actual.reverse();
}

const allOneLine = (base, hint) => {
    let record = clone(base);

    if (hint.length === hint.filter(item => item === 1).length) {
        for (let i = 0; i < record.length; i++) {
            if (record[i] === 1) {
                fill(record, -1, i - 1);
                fill(record, -1, i + 1);
            }
        }
    }

    return record;
}

const _shorten = (base, hint) => {
    let record = clone(base);
    let count = 0, hintOffset = 0, offset = -1;

    for (let i = 0, flag = true; i < record.length && flag; i++) {
        switch (record[i]) {
            case -1:
                if (count > 0) {
                    if (count !== hint[hintOffset]) {
                        console.log('Error splitting');
                    } else {
                        count = 0;
                        hintOffset += 1;
                    }
                }
                offset = i;
                break;
            case 0:
                // offset = i;
                flag = false;
                break;
            case 1:
                count++;
                break;
            default:
                break;
        }
    }

    offset++;

    record = record.slice(offset);
    hint = hint.slice(hintOffset);

    return [offset, hintOffset];
}

const shorten = (base, hint) => {
    let record = clone(base);
    let _hint = clone(hint);

    let [lOffset, lHintOffset] = _shorten(record, _hint)

    record = record.reverse();
    _hint = _hint.reverse();

    let [rOffset, rHintOffset] = _shorten(record, _hint)

    record = record.reverse();
    _hint = _hint.reverse()

    return {
        record: record.slice(lOffset, record.length - rOffset),
        _hint: _hint.slice(lHintOffset, _hint.length - rHintOffset),
        lOffset,
        rOffset,
    };
}

const divide = base => {
    let record = clone(base);
    let tempArr = [], offset = 0, res = [];
    for (let i = 0; i < record.length; i++) {
        if (record[i] === -1) {
            if (tempArr.length) {
                res.push({
                    seg: clone(tempArr),
                    offset,
                })
                tempArr = [];
            }
            offset = i + 1;
        } else {
            tempArr.push(record[i])
        }
    }

    if (tempArr.length) {
        res.push({
            seg: clone(tempArr),
            offset,
        })
    }
    return res;
}

const generalLine = (base, hint) => {
    let { record, _hint, lOffset } = shorten(base, hint)

    record = knownLine(record, _hint)
    record = allOneLine(record, _hint)

    // if (isShow) { console.log(printLine(base), "  ", printLine(record)); console.log(_hint); console.log('???') }

    let arr = divide(record);

    if (_hint.length === 1) {
        if (record.indexOf(1) !== -1) {
            let l = record.indexOf(1); // 1
            let r = record.lastIndexOf(1); // 3

            if (l <= r) {
                fillRange(record, 1, l, _hint[0] - l - 1);
                fillRange(record, -1, _hint[0] + l, record.length - 1);
            } else {
                record.reverse();
                fillRange(record, 1, r, _hint[0] - r - 1);
                fillRange(record, -1, _hint[0] + r, record.length - 1);
                record.reverse()
            }
        }
    } else {
        // console.log(arr, _hint)
        if (arr.length === 2 && _hint.length === 2) {
            let _total = _.sum(_hint) + 1;

            // console.log(arr, _hint, _total)

            if (!arr.some(item => item.seg.length >= _total)) {
                for (let i = 0; i < arr.length; i++) {
                    let _temp = knownLine(arr[i].seg, [_hint[i]]);
                    // console.log(arr[i].seg, _hint[i], _temp)

                    for (let j = 0; j < _temp.length; j++) {
                        if (isShow) {
                            console.log();
                        }
                        fill(record, _temp[j], arr[i].offset + j);
                    }
                }

            }
        }
    }

    if (isShow) { console.log(printLine(base), "  ", printLine(record)); console.log(_hint); console.log('???') }

    for (let i = 0; i < record.length; i++) {
        base[lOffset + i] = record[i]
    }

    return base;
}

const setIsShow = flag => isShow = flag;

module.exports = {
    knownLine,
    edgeLine,
    allOneLine,
    generalLine,
    setIsShow,
}